package ua.pp.fland.multithreading.sorting;

import org.apache.log4j.Logger;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Maxim Bondarenko
 * @version 1.0 Jun 29, 2010
 */

public class SortingController {
    private static final Logger logger = Logger.getLogger(SortingController.class);

    private final int threadsNum;

    private final DataProcessor dataProcessor;

    public SortingController(int threadsNum) {
        ArgumentGuard.checkPositiveInteger(threadsNum, "threadsNum");
        this.threadsNum = threadsNum;

        dataProcessor = new NumberDataProcessor();
    }

    public List<Integer> sort(List<Integer> unsortedData) throws IllegalStateException, InterruptedException {
        ArgumentGuard.checkNotNull(unsortedData, "unsortedData");
        List<Integer> res = new ArrayList<Integer>(unsortedData);

        for (int i = 0; i < res.size(); i++) {
            //forming even pairs
            res = new ArrayList<Integer>(this.pairSort(res, this.threadsNum));

            //forming odd pairs
            List<Integer> oddPairs = new ArrayList<Integer>();
            for (int j = 1; j < res.size(); j++) {
                oddPairs.add(res.get(j));
            }
            int temp = res.get(0);
            res = new ArrayList<Integer>() ;
            res.add(temp);
            res.addAll(this.pairSort(oddPairs, this.threadsNum));
            i++;
        }
        logger.debug("Sorting finished...");
        logger.debug("Sorting res: " + res);
        return res;
    }

    private List<Integer> pairSort(List<Integer> inputData, int threadsNum) throws InterruptedException{
        //forming data groups
        List<List<Integer>> groupedData = new ArrayList<List<Integer>>(this.dataProcessor.groupUnsortedData(inputData, threadsNum));
        //starting sorting
        //creating threads
        List<ThreadsRunner> threadsRunners = new ArrayList<ThreadsRunner>();
        List<Thread> threads = new ArrayList<Thread>();
        for (int i = 0; i < threadsNum; i++) {
            threadsRunners.add(new ThreadsRunner(groupedData.get(i)));
            threads.add(new Thread(threadsRunners.get(i), "Thread number" + i));
            threads.get(i).start();
        }

        //waiting for threads
        for (Thread thread : threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {
                logger.error("Can't wait thread's finish. Error: " + e, e);
                throw new InterruptedException(e.getMessage());
            }
        }

        //forming full data
        List<Integer> res = new ArrayList<Integer>();
        for (int i = 0; i < threadsNum; i++) {
            res.addAll(threadsRunners.get(i).getOutputData());
        }
        return res;
    }
}
